package com.niwodai.fintrade.report.configuration;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @Author syh
 * @date 2023/11/28
 * 自定义元数据对象处理器
 */
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {

    private static final String[] createTimeField = {"createTime", "createdTime"};

    private static final String[] updateTimeField = {"updateTime", "updatedTime"};

    private static final String[] createUserField = {"createUser", "createdBy"};

    private static final String[] updateUserField = {"updateUser", "updatedBy"};

    @Value("${fintrade.user.name:fintrade-job}")
    private String jobUserName;

    /**
     * 插入操作自动填充
     *
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {

        Arrays.stream(createTimeField).forEach(fieldName -> {
            if (metaObject.hasSetter(fieldName)) {
                String typeName = metaObject.getGetterType(fieldName).getName();
                if (typeName.equals(LocalDateTime.class.getName())) {
                    setFieldValByName(fieldName, LocalDateTime.now(), metaObject);
                } else {
                    setFieldValByName(fieldName, new Date(), metaObject);
                }
            }
        });

        Arrays.stream(updateTimeField).forEach(fieldName -> {
            if (metaObject.hasSetter(fieldName)) {
                String typeName = metaObject.getGetterType(fieldName).getName();
                if (typeName.equals(LocalDateTime.class.getName())) {
                    setFieldValByName(fieldName, LocalDateTime.now(), metaObject);
                } else {
                    setFieldValByName(fieldName, new Date(), metaObject);
                }
            }
        });

        String userName = Optional.ofNullable(RequestThreadLocal.get()).map(RequestThreadLocal.CurrentUser::getUserName).orElse(jobUserName);
        Arrays.stream(createUserField).forEach(fieldName -> {
            if (metaObject.hasSetter(fieldName)) {
                setFieldValByName(fieldName, userName, metaObject);
            }
        });
        Arrays.stream(updateUserField).forEach(fieldName -> {
            if (metaObject.hasSetter(fieldName)) {
                setFieldValByName(fieldName, userName, metaObject);
            }
        });

    }

    /**
     * 更新操作自动填充
     *
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {

        Arrays.stream(updateTimeField).forEach(fieldName -> {
            if (metaObject.hasSetter(fieldName)) {
                fillCreateAndUpdateTime(metaObject, fieldName);
            }
        });

        String userName = Optional.ofNullable(RequestThreadLocal.get()).map(RequestThreadLocal.CurrentUser::getUserName).orElse(jobUserName);

        Arrays.stream(updateUserField).forEach(fieldName -> {
            if (metaObject.hasSetter(fieldName)) {
                setFieldValByName(fieldName, userName, metaObject);
            }
        });
    }


    private void fillCreateAndUpdateTime(MetaObject metaObject, String fieldName) {
        HashMap originalObject = (HashMap) metaObject.getOriginalObject();
        if (originalObject.get("param1") != null) {
            Class<?> className = originalObject.get("param1").getClass();
            extracted(metaObject, fieldName, className);
        }
    }

    /**
     * @param metaObject
     * @param fieldName
     * @param className
     */
    private void extracted(MetaObject metaObject, String fieldName, Class<?> className) {
        List<String> fieldNameList = new ArrayList<>();
        List<Field> collect = getAllFields(className);
        for (Field field : collect) {
            if (field.getName().equals(fieldName)) {
                fieldNameList.add(fieldName);
            }
        }

        if (CollectionUtils.isNotEmpty(fieldNameList)) {
            fieldNameList.forEach(item -> {
                String typeName;
                try {
                    typeName = collect.stream().filter(field -> field.getName().equals(item)).findFirst().get().getType().getName();
                    if (typeName.equals(LocalDateTime.class.getName())) {
                        setFieldValByName(item, LocalDateTime.now(), metaObject);
                    } else {
                        setFieldValByName(item, new Date(), metaObject);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
    }

    private List<Field> getAllFields(Class<?> clazz) {
        List<Field> fields = new ArrayList<>();
        while (clazz != null) {
            fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
            clazz = clazz.getSuperclass(); // move to the superclass
        }
        return fields;
    }
}
